<?php
class Type_Abstract {
	
	const WIRETYPE_VARINT = 0;
	const WIRETYPE_FIXED64 = 1;
	const WIRETYPE_LENGTH_DELIMITED = 2;
	const WIRETYPE_START_GROUP = 3;
	const WIRETYPE_END_GROUP = 4;
	const WIRETYPE_FIXED32 = 5;
	
	protected $value = null;
	protected $extra;
	protected $rule;
	protected $index;
	protected $wireType;
	protected $type;
	protected static $wireTypes = array("Varint", "Fixed64", "LenghtDelimited", "StartGroup", "EndGroup", "Fixed32");
	
	public static function getWireTypeNameById($id) {
		return self::$wireTypes[$id];
	}
	public static function create($type, $args) {
		$class = "Type_" . Type::getNameById($type) ;
		if(!class_exists($class)) { throw new Type_Exception("type '$class' not found"); }
		return new $class($args['index'], $args['rule'], $args['extra']);
	}	
	public function __construct($index, $rule, $extra) {
		$this->index = $index;
		$this->rule = $rule;
		$this->extra = $extra;
	}
	public function setValue($value) {
		$this->value = $value;
	}
	public function getValue() {
		return $this->value;
	}
	public function read(IO_Reader_Interface $reader) {
		//$this->checkFieldInfo($reader);
		$this->readImpl($reader);
	}
	public function write(IO_Writer_Interface $writer) { 
		if($this->rule == Rule::OPTIONAL && $this->value === null) { return; }
		$this->writeFieldInfo($writer);
		$this->writeImpl($writer);
	}
	public function getWireType() {
		/**
		 * return $this->wireType; not work, returned null. May by php bug?
		 */
		$wt = $this->wireType;
		return $wt;
	}
	protected function readImpl(IO_Reader_Interface $reader) {
		throw new Type_Exception("you mast override function Type_Abstract#readImpl");
	}
	protected function writeImpl(IO_Writer_Interface $writer) { 
		throw new Type_Exception("you mast override function Type_Abstract#writeImpl");
	}
	protected function readWireTypeData(IO_Reader_Interface $reader) {
		return call_user_func_array(array("WireType_" . self::getWireTypeNameById($this->wireType), "read"), array($reader));
	}
	protected function writeWireTypeData(IO_Writer_Interface $writer, $value) {
		call_user_func_array(array("WireType_" . self::getWireTypeNameById($this->wireType), "write"), array($writer, $value));
	}
	
	protected function checkFieldInfo($reader) {
		$varint = Base128::decodeFromReader($reader);
		$bigMask = bindec("111");
		$wireType = $varint & $bigMask;
		$fieldIndex = $varint >> 3;
		if($fieldIndex != $this->index && $this->rule == Rule::REQUIRED) { throw new Type_Exception("discrepancy of indexes"); }
		if($wireType != $this->wireType && $this->rule == Rule::REQUIRED) { throw new Type_Exception("discrepancy of wire types"); }
	}
	protected function writeFieldInfo($writer) {
		$value = $this->index << 3;
		$value = $value | $this->wireType;
		Base128::encodeToWriter($writer, $value);
	}
}
?>